package com.violet.uaa.server.security.config;

import com.violet.auth.client.properties.SecurityProperties;
import com.violet.auth.client.token.CustomWebAuthenticationDetails;
import com.violet.common.config.DefaultPasswordConfig;
import com.violet.common.constant.universal.SecurityConstants;
import com.violet.uaa.server.security.factory.UserDetailServiceFactory;
import com.violet.uaa.server.security.filter.JwtAuthenticationTokenFilter;
import com.violet.uaa.server.security.filter.SecurityCaptchaFilter;
import com.violet.uaa.server.security.handler.SimpleAccessDeniedHandler;
import com.violet.uaa.server.security.handler.SimpleAuthenticationEntryPoint;
import com.violet.uaa.server.security.handler.VioLogOutSuccessHandler;
import com.violet.uaa.server.security.handler.VioSuccessHandler;
import com.violet.uaa.server.security.password.PasswordAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * 类说明: spring security 核心配置
 *
 * @author wqf
 * @date 2023/6/14 11:16
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@Import(DefaultPasswordConfig.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private VioLogOutSuccessHandler vioLogOutSuccessHandler;
    @Resource
    private SimpleAccessDeniedHandler simpleAccessDeniedHandler;
    @Resource
    private SimpleAuthenticationEntryPoint simpleAuthenticationEntryPoint;
    @Resource
    private SecurityProperties securityProperties;
    @Resource
    private SecurityCaptchaFilter securityCaptchaFilter;
    @Resource
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Resource
    private VioSuccessHandler vioSuccessHandler;
    @Resource
    private UserDetailServiceFactory userDetailsServiceFactory;
    @Resource
    private AuthenticationDetailsSource<HttpServletRequest, CustomWebAuthenticationDetails> authenticationDetailsSource;

    @Autowired(required = false)
    private AuthenticationEntryPoint authenticationEntryPoint;

    /**
     * http请求拦截认证
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //登录相关配置
        http.formLogin()
                .loginPage(SecurityConstants.LOGIN_PAGE)
                .loginProcessingUrl(SecurityConstants.LOGIN_URL)
                .successHandler(vioSuccessHandler)
                .authenticationDetailsSource(authenticationDetailsSource);
        //登出相关配置
        http.logout()
                //登出地址
                .logoutUrl(SecurityConstants.LOGOUT_URL)
                //登出成功重定向地址
                .logoutSuccessUrl(SecurityConstants.LOGOUT_SUCCESS_URL)
                //登出成功自定义处理器
                .logoutSuccessHandler(vioLogOutSuccessHandler)
                //注销成功，是否清除当前用户，默认值是true
                .clearAuthentication(true);
        //异常处理
        http.exceptionHandling()
                .accessDeniedHandler(simpleAccessDeniedHandler)
                .authenticationEntryPoint(simpleAuthenticationEntryPoint);
        //权限校验配置
        http.authorizeRequests()
                //白名单路径放行
                .antMatchers(securityProperties.getIgnore().getUrls())
                //授权服务器关闭basic认证
                .permitAll()
                //其他所有请求需要身份认证
                .anyRequest()
                .authenticated()
                .and()
                .csrf().disable();

        // 基于密码 等模式可以无session,不支持授权码模式
        if (authenticationEntryPoint != null) {
            http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        } else {
            // 授权码模式单独处理，需要session的支持，此模式可以支持所有oauth2的认证
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        }

        //添加自定义JWT过滤器
        http.addFilterBefore(securityCaptchaFilter, UsernamePasswordAuthenticationFilter.class);
        //token校验过滤器，在验证码校验过滤器之前执行
        // UsernamePasswordAuthenticationFilter 是 Spring Security 提供的一个默认注册的过滤器，用于处理基于表单的登录认证。
        // 当用户尝试登录时，这个过滤器会捕获登录请求，提取用户名和密码，并尝试使用这些凭证进行认证。
        //JWT 拦截器的目的是从请求头中提取 JWT，并基于该 JWT 进行认证。如果请求已经包含一个有效的 JWT，那么用户就已经被认证，不需要再进行基于表单的登录。
        // 因此，JWT 拦截器应该在 UsernamePasswordAuthenticationFilter 之前运行，
        // 这样如果 JWT 认证成功，就不需要进入后续的基于表单的认证过程了。
        http.addFilterBefore(jwtAuthenticationTokenFilter, SecurityCaptchaFilter.class);
    }


    /**
     * AuthenticationManager对象在OAuth2认证服务中要使用，提前放入IOC容器中
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    /**
     * 静态资源放行
     */
    @Override
    public void configure(WebSecurity webSecurity) {
        // 静态资源放行
        webSecurity.ignoring().antMatchers("/dist/**", "/moudle/**", "/plugins/**");
    }

    /**
     * 全局用户信息
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) {
        PasswordAuthenticationProvider provider = new PasswordAuthenticationProvider();
        provider.setPasswordEncoder(passwordEncoder);
        provider.setUserDetailsServiceFactory(userDetailsServiceFactory);
        auth.authenticationProvider(provider);
    }
}